﻿using System;
using System.Collections.Generic;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using Citi.GocAttributes.Domain.Utils;

namespace Citi.GocAttributes.WebUI.Models
{
    public class ModelBase
    {
        public const string DateFormat = "dd/MM/yyyy";
        public int Id { get; set; }
    }

    public class ModelBase<T, TEntity> : ModelBase
        where T : new()
        where TEntity : GeneralAction, new()
    {

        public TEntity CreateDomain(TEntity domain = default(TEntity))
        {
            if (domain == null)
                domain = new TEntity();

            foreach (var property in domain.GetType().GetProperties().Where(p => !p.Name.Contains("Key") && !p.Name.EndsWith("Reference")))
            {
                var propertyModel = typeof(T).GetProperty(property.Name);
                var propertyDomain = typeof(TEntity).GetProperty(property.Name);

                if (propertyModel == null || propertyDomain == null || propertyDomain.GetSetMethod() == null) continue;

                if (propertyModel == null) continue;

                var valor = propertyModel.GetValue(this, null);

                if (property.PropertyType.Namespace.Contains("Domain"))
                {
                    if (valor == null) continue;

                    var method = propertyModel.PropertyType.GetMethod("CreateDomain");

                    if (method.IsGenericMethodDefinition)
                    {
                        var methodGeneric =
                            propertyModel.PropertyType.GetMethod("CreateDomain").MakeGenericMethod(new[] { property.PropertyType });
                        valor = methodGeneric.Invoke(valor, new[] { property.GetValue(domain, null) });
                    }
                    else
                        valor = method.Invoke(valor, new[] { property.GetValue(domain, null) });
                }

                property.SetValue(domain, valor, null);
            }

            return domain;
        }

        public IEnumerable<T> CreateModel(IEnumerable<EntityObject> domain)
        {
            //foreach (var item in domain)
            //{
            //    CreateModel(item);
            //}

            //return null;
            return domain.Select(CreateModel).ToList();
        }

        public T CreateModel(EntityObject domain)
        {
            if (domain == null) return default(T);

            var model = new T();

            foreach (var property in model.GetType().GetProperties())
            {
                try
                {
                    var propertyDomain = domain.GetType().GetProperty(property.Name);
                    if (propertyDomain == null) continue;

                    var valueDomain = propertyDomain.GetValue(domain, null);
                    if (valueDomain == null) continue;

                    if (valueDomain.GetType().Namespace.Contains("Domain"))
                    {
                        var method = property.PropertyType.GetMethod("CreateModel", new[] { typeof(EntityObject) });
                        var methodReturn = method.Invoke(Activator.CreateInstance(property.PropertyType),
                                                          new[] { propertyDomain.GetValue(domain, null) });
                        property.SetValue(model, methodReturn, null);
                    }
                    else if (valueDomain.GetType().FullName.Contains("EntityCollection") || valueDomain.GetType().FullName.Contains("Enumerable"))
                    {
                        var listType = property.PropertyType.FullName.Split(new[] { '[', ']', ',' }, StringSplitOptions.RemoveEmptyEntries);
                        var type = Activator.CreateInstance(listType[2], listType[1]).Unwrap();
                        var method = type.GetType().GetMethod("CreateModel", new[] { typeof(List<EntityObject>) });
                        var methodReturn = method.Invoke(type, new[] { propertyDomain.GetValue(domain, null) });
                        property.SetValue(model, methodReturn, null);
                    }
                    else
                        property.SetValue(model, propertyDomain.GetValue(domain, null), null);
                }
                catch
                {
                    continue;
                }
            }

            return model;
        }

        public T Converter(FormCollection collection)
        {
            var domain = Activator.CreateInstance(typeof(T));

            foreach (string key in collection.Keys)
            {
                var property = domain.GetType().GetProperty(key);

                try
                {
                    if (property != null)
                    {
                        if (property.PropertyType == typeof(Decimal) ||
                            property.PropertyType == typeof(decimal))
                            property.SetValue(domain, Decimal.Parse(collection[key]), null);
                        else if (property.PropertyType == typeof(Boolean) ||
                                 property.PropertyType == typeof(bool))
                            property.SetValue(domain, Boolean.Parse(collection[key].Split(',').First()), null);
                        else if (property.PropertyType == typeof(Int16) ||
                                 property.PropertyType == typeof(Int32) ||
                                 property.PropertyType == typeof(Int64) ||
                                 property.PropertyType == typeof(uint) ||
                                 property.PropertyType == typeof(UInt16) ||
                                 property.PropertyType == typeof(UInt32) ||
                                 property.PropertyType == typeof(UInt64) ||
                                property.PropertyType == typeof(int?))
                            property.SetValue(domain, int.Parse(collection[key]), null);
                        else if (property.PropertyType == typeof(DateTime))
                            property.SetValue(domain, DateTime.Parse(collection[key]), null);
                        else if (property.PropertyType == typeof(char))
                            property.SetValue(domain, char.Parse(collection[key]), null);
                        else
                            property.SetValue(domain, collection[key], null);
                    }
                }
                catch
                {
                }
            }

            return (T)domain;
        }

        public string GetParameterValue(string url, string paramName)
        {
            var uri = new Uri(url);
            if (!string.IsNullOrEmpty(uri.Query) && uri.Query.ToLower().Contains(paramName))
            {
                Match m = Regex.Match(uri.Query, "^?" + paramName + "=([^&#]*)");
                if (m.Success) return m.Value.Replace(paramName + "=", string.Empty);
            }

            return url.Split('/').Last();
        }
    }
}
